package comm

import "io"
import "bytes"
import "encoding/binary"
import log "github.com/golang/glog"
import "github.com/bitly/go-simplejson"
import "fmt"
import "errors"
import "github.com/garyburd/redigo/redis"
import "time"

//维持客户端服务器之间心跳
const MSG_HEARTBEAT = 1	
//认证TOKEN
const MSG_AUTH_TOKEN = 2
//服务器认证完返回
const MSG_AUTH_STATUS = 3
//普通聊天消息
const MSG_IM = 4
//普通聊天消息确认，返回给客户端
const MSG_ACK = 5
//群通知
const MSG_GROUP_NOTIFICATION = 6
//普通群消息
const MSG_GROUP_IM = 7
//客户端发送给服务器告诉服务器消息已经到达
const MSG_PEER_ACK = 8
//正在输入状态
const MSG_INPUTING = 9
//在线状态
const MSG_ONLINE_STATE = 10
//客户端发出的心跳PING
const MSG_PING = 11
//服务器响应客户端PONG
const MSG_PONG = 12
//登录设备信息
const MSG_LOGIN_POINT = 13
//设备互踢
const MSG_DEVICE_KICK = 14
//聊天室消息
const MSG_ROOM_IM = 15
//透传消息
const MSG_THROUGH_IN = 16
const MSG_THROUGH_OUT = 17
//透传消息ack
const MSG_PEER_ACK_CMD = 18
//好友相关
const MSG_FRIEND_ADD = 19//添加好友
const MSG_FRIEND_ADDED = 20//被添加好友
const MSG_FRIEND_DEL = 21//删除好友
const MSG_FRIEND_DELED = 22//被删除好友
const MSG_FRIEND_GETLIST = 23//获取好友列表
const MSG_FRIEND_LIST = 24//获取好友列表

const PLATFORM_IOS = 1
const PLATFORM_ANDROID = 2
const PLATFORM_WEB = 3

const DEFAULT_VERSION = 1

//透传消息类型
const THROUGH_TARGET_USER = 1//用户
const THROUGH_TARGET_GROUP= 2//群组
const THROUGH_TARGET_ROOM = 3//聊天室

var message_descriptions map[int]string = make(map[int]string)

type MessageCreator func()IMessage
var message_creators map[int]MessageCreator = make(map[int]MessageCreator)


func InitMessage() {
	message_creators[MSG_AUTH_STATUS] = func()IMessage {return new(AuthenticationStatus)}

	message_creators[MSG_ACK] = func()IMessage{return new(MessageACK)}
	message_creators[MSG_GROUP_NOTIFICATION] = func()IMessage{return new(GroupNotification)}

	message_creators[MSG_PEER_ACK] = func()IMessage{return new(MessagePeerACK)}
	message_creators[MSG_PEER_ACK_CMD] = func()IMessage{return new(MessagePeerACK)}
	message_creators[MSG_INPUTING] = func()IMessage{return new(MessageInputing)}
	message_creators[MSG_ONLINE_STATE] = func()IMessage{return new(MessageOnlineState)}
	message_creators[MSG_AUTH_TOKEN] = func()IMessage{return new(AuthenticationToken)}
	
	message_creators[MSG_GROUP_IM] = func()IMessage{return new(IMMessage)}
	message_creators[MSG_IM] = func()IMessage{return new(IMMessage)}
	message_creators[MSG_ROOM_IM] = func()IMessage{return new(IMMessage)}
	
	message_creators[MSG_DEVICE_KICK] = func()IMessage{return new(DeviceKick)}
	message_creators[MSG_THROUGH_IN] = func()IMessage{return new(ThroughMessageIn)}
	message_creators[MSG_THROUGH_OUT] = func()IMessage{return new(ThroughMessageOut)}
	
	message_creators[MSG_FRIEND_ADD] = func()IMessage{return new(FriendOp)}
	message_creators[MSG_FRIEND_ADDED] = func()IMessage{return new(FriendOpStatus)}
	message_creators[MSG_FRIEND_DEL] = func()IMessage{return new(FriendOp)}
	message_creators[MSG_FRIEND_DELED] = func()IMessage{return new(FriendOpStatus)}
	message_creators[MSG_FRIEND_LIST] = func()IMessage{return new(FriendList)}

	message_descriptions[MSG_AUTH_STATUS] = "MSG_AUTH_STATUS"
	message_descriptions[MSG_IM] = "MSG_IM"
	message_descriptions[MSG_ACK] = "MSG_ACK"
	message_descriptions[MSG_GROUP_NOTIFICATION] = "MSG_GROUP_NOTIFICATION"
	message_descriptions[MSG_GROUP_IM] = "MSG_GROUP_IM"
	message_descriptions[MSG_ROOM_IM] = "MSG_ROOM_IM"
	message_descriptions[MSG_PEER_ACK] = "MSG_PEER_ACK"
	message_descriptions[MSG_INPUTING] = "MSG_INPUTING"
	message_descriptions[MSG_ONLINE_STATE] = "MSG_ONLINE_STATE"
	message_descriptions[MSG_PING] = "MSG_PING"
	message_descriptions[MSG_PONG] = "MSG_PONG"
	message_descriptions[MSG_AUTH_TOKEN] = "MSG_AUTH_TOKEN"
	message_descriptions[MSG_LOGIN_POINT] = "MSG_LOGIN_POINT"
	message_descriptions[MSG_DEVICE_KICK] = "MSG_DEVICE_KICK"
	message_descriptions[MSG_THROUGH_IN] = "MSG_THROUGH_IN"
	message_descriptions[MSG_THROUGH_OUT] = "MSG_THROUGH_OUT"
}

type Command int
func (cmd Command) String() string {
	c := int(cmd)
	if desc, ok := message_descriptions[c]; ok {
		return desc
	} else {
		return fmt.Sprintf("%d", c)
	}
}

type IMessage interface {
	ToData() []byte
	FromData(buff []byte) bool
}

type Message struct {
	Cmd  int
	Seq  int64
	Version int
	
	Body interface{}
}

func (message *Message) ToData() []byte {
	if message.Body != nil {
		if m, ok := message.Body.(IMessage); ok {
			return m.ToData()
		}
		return nil
	} else {
		return nil
	}
}

func (message *Message) FromData(buff []byte) bool {
	cmd := message.Cmd
	if creator, ok := message_creators[cmd]; ok {
		c := creator()
		r := c.FromData(buff)
		message.Body = c
		return r
	}

	return len(buff) == 0
}

func (message *Message) ToMap() map[string]interface{} {
	data := make(map[string]interface{})
	data["cmd"] = message.Cmd
	data["seq"] = message.Seq
	cmd := message.Cmd
	if cmd == MSG_AUTH_STATUS {
		body := message.Body.(*AuthenticationStatus)
		data["body"] = map[string]interface{}{
			"status": body.Status,
		}
	} else if cmd == MSG_IM || cmd == MSG_GROUP_IM {
		body := message.Body.(*IMMessage)
		data["body"] = map[string]interface{}{
			"sender":    body.Sender,
			"receiver":  body.Receiver,
			"timestamp": body.Timestamp,
			"msgid":     body.Msgid,
			"content":   body.Content,
			"extra":	 body.Extra,
		}
	} else if cmd == MSG_ACK {
		data["body"] = message.Body.(*MessageACK).Seq
	} else if cmd == MSG_PEER_ACK {
		body := message.Body.(*MessagePeerACK)
		data["body"] = map[string]interface{}{
			"sender":   body.Sender,
			"receiver": body.Receiver,
			"msgid":    body.Msgid,
		}
	} else if cmd == MSG_HEARTBEAT || cmd == MSG_PING || cmd == MSG_PONG {
		data["body"] = nil
	} else if cmd == MSG_INPUTING {
		body := message.Body.(*MessageInputing)
		data["body"] = map[string]interface{}{
			"sender":   body.Sender,
			"receiver": body.Receiver,
		}
	} else if cmd == MSG_GROUP_NOTIFICATION {
		data["body"] = message.Body.(*GroupNotification).Notification
	} else if cmd == MSG_ONLINE_STATE {
		body := message.Body.(*MessageOnlineState)
		data["body"] = map[string]interface{}{
			"sender": body.Sender,
			"online": body.Online,
		}
	} else {
		data["body"] = nil
	}
	return data
}

func (message *Message) FromJson(msg *simplejson.Json) bool {
	switch message.Cmd {
	case MSG_AUTH_TOKEN:
		uid, err := msg.Get("body").Get("uid").Int64()
		if err != nil {
			log.Info("get uid fail")
			return false
		}
		token, err := msg.Get("body").Get("access_token").String()
		if err != nil {
			log.Info("get access token fail")
			return false
		}
		platform_id, err := msg.Get("body").Get("platform_id").Int()
		if err != nil {
			log.Info("get platform id fail")
			return false
		}
		device_id, err := msg.Get("body").Get("device_id").String()
		if err != nil {
			log.Info("get device id fail")
			return false
		}
		data := &AuthenticationToken{}
		data.Uid = uid
		data.Token = token
		data.Platform_id = int8(platform_id)
		data.Device_id = device_id
		message.Body = data
		return true
	case MSG_AUTH_STATUS:
		status, err := msg.Get("body").Get("status").Int()
		if err != nil {
			log.Info("get status fail")
			return false
		}

		data := &AuthenticationStatus{}
		data.Status = int32(status)
		message.Body = data
		return true

	case MSG_IM, MSG_GROUP_IM:
		sender, err := msg.Get("body").Get("sender").Int64()
		if err != nil {
			log.Info("get sender fail")
			return false
		}

		receiver, err := msg.Get("body").Get("receiver").Int64()
		if err != nil {
			log.Info("get receiver fail")
			return false
		}

		timestamp := msg.Get("body").Get("timestamp").MustInt(0)

		msgid, err := msg.Get("body").Get("msgid").Int()
		if err != nil {
			log.Info("get msgid fail")
			return false
		}

		content, err := msg.Get("body").Get("content").String()
		if err != nil {
			log.Info("get content fail")
			return false
		}
		
		extra, err := msg.Get("body").Get("extra").String()
		if err != nil {
			log.Info("get content fail")
			return false
		}
		
		data := &IMMessage{}
		data.Sender = sender
		data.Receiver = receiver
		data.Timestamp = int32(timestamp)
		data.Msgid = int64(msgid)
		data.Content = content
		data.Extra = extra
		message.Body = data
		return true
	case MSG_ACK:
		body, err := msg.Get("body").Int()
		if err != nil {
			log.Info("read body fail")
			return false
		}

		message.Body = &MessageACK{int64(body)}
		return true
	case MSG_HEARTBEAT, MSG_PING, MSG_PONG:
		return true
	case MSG_INPUTING:
		sender, err := msg.Get("body").Get("sender").Int64()
		if err != nil {
			log.Info("get sender fail")
			return false
		}

		receiver, err := msg.Get("body").Get("receiver").Int64()
		if err != nil {
			log.Info("get receiver fail")
			return false
		}

		data := &MessageInputing{}
		data.Sender = sender
		data.Receiver = receiver
		message.Body = data
		return true

	case MSG_GROUP_NOTIFICATION:
		body, err := msg.Get("body").String()
		if err != nil {
			log.Info("read body fail")
			return false
		}
		
		message.Body = &GroupNotification{body}
		return true
	case MSG_PEER_ACK:
		sender, err := msg.Get("body").Get("sender").Int64()
		if err != nil {
			log.Info("get sender fail")
			return false
		}

		receiver, err := msg.Get("body").Get("receiver").Int64()
		if err != nil {
			log.Info("get receiver fail")
			return false
		}

		msgid, err := msg.Get("body").Get("msgid").Int()
		if err != nil {
			log.Info("get msgid fail")
			return false
		}

		data := &MessagePeerACK{}
		data.Sender = sender
		data.Receiver = receiver
		data.Msgid = int64(msgid)
		message.Body = data
		return true
	default:
		return false
	}
}

func WriteHeader(len int32, seq int64, cmd byte, version byte, buffer *bytes.Buffer) {
	binary.Write(buffer, binary.BigEndian, len)
	binary.Write(buffer, binary.BigEndian, seq)
	buffer.WriteByte(cmd)
	buffer.WriteByte(version)
	buffer.WriteByte(byte(0))
	buffer.WriteByte(byte(0))
}

func ReadHeader(buff []byte) (int, int64, int, int) {
	var length int32
	var seq int64
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &length)
	binary.Read(buffer, binary.BigEndian, &seq)
	cmd, _ := buffer.ReadByte()
	version, _ := buffer.ReadByte()
	return int(length), int64(seq), int(cmd), int(version)
}

type RTMessage struct {
	sender    int64
	receiver  int64
	content   string
}
func (message *RTMessage) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, message.sender)
	binary.Write(buffer, binary.BigEndian, message.receiver)
	buffer.Write([]byte(message.content))
	buf := buffer.Bytes()
	return buf
}

func (rt *RTMessage) FromData(buff []byte) bool {
	if len(buff) < 16 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &rt.sender)
	binary.Read(buffer, binary.BigEndian, &rt.receiver)
	rt.content = string(buff[16:])
	return true
}

type IMMessage struct {
	Sender    int64
	Receiver  int64
	Gid		  int64
	Timestamp int32
	Msgid     int64
	Content   string
	Extra     string
}

func (message *IMMessage) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, message.Sender)
	binary.Write(buffer, binary.BigEndian, message.Receiver)
	binary.Write(buffer, binary.BigEndian, message.Gid)
	binary.Write(buffer, binary.BigEndian, message.Timestamp)
	binary.Write(buffer, binary.BigEndian, message.Msgid)
	content := []byte(message.Content)
	binary.Write(buffer, binary.BigEndian, int32(len(content)))
	extra := []byte(message.Extra)
	binary.Write(buffer, binary.BigEndian, int32(len(extra)))
	buffer.Write(content)
	buffer.Write(extra)
	buf := buffer.Bytes()
	return buf
}

func (im *IMMessage) FromData(buff []byte) bool {
	if len(buff) < 44 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &im.Sender)
	binary.Read(buffer, binary.BigEndian, &im.Receiver)
	binary.Read(buffer, binary.BigEndian, &im.Gid)
	binary.Read(buffer, binary.BigEndian, &im.Timestamp)
	binary.Read(buffer, binary.BigEndian, &im.Msgid)
	
	var len_content, len_extra int32
	binary.Read(buffer, binary.BigEndian, &len_content)
	binary.Read(buffer, binary.BigEndian, &len_extra)
	
	im.Content = string(buff[44:(44 + len_content)])
	im.Extra = string(buff[(44 + len_content):(44 + len_content + len_extra)])
	
	return true
}

type ThroughMessageIn struct {
	Sender    int64 //发送者
	Target_type int8 //目标类型，用户或群或聊天室
	Targets_len int32 //目标个数，不超过一定数量
	Targets		[]int64 //目标列表
	Action   string //action
	Extra     string //附加内容
}

func (tmi *ThroughMessageIn) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, tmi.Sender)
	binary.Write(buffer, binary.BigEndian, tmi.Target_type)
	binary.Write(buffer, binary.BigEndian, tmi.Targets_len)
	for i := 0; i < int(tmi.Targets_len); i++ {
		binary.Write(buffer, binary.BigEndian, tmi.Targets[i])
	}
	l1 := int8(len(tmi.Action))
	binary.Write(buffer, binary.BigEndian, l1)
	buffer.Write([]byte(tmi.Action))
	l2 := int32(len(tmi.Extra))
	binary.Write(buffer, binary.BigEndian, l2)
	if l2 > 0 {
		buffer.Write([]byte(tmi.Extra))
	}
	buf := buffer.Bytes()
	return buf
}

func (tmi *ThroughMessageIn) FromData(buff []byte) bool {
	if len(buff) < 13 {
		return false
	}
	
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &tmi.Sender)
	binary.Read(buffer, binary.BigEndian, &tmi.Target_type)
	binary.Read(buffer, binary.BigEndian, &tmi.Targets_len)
	if tmi.Targets_len > 0 {
		tmi.Targets = make([]int64, tmi.Targets_len)
		for i := 0; i < int(tmi.Targets_len); i++ {
			binary.Read(buffer, binary.BigEndian, &tmi.Targets[i])
		}
	}
	var l1 int8
	binary.Read(buffer, binary.BigEndian, &l1)
	action := make([]byte, l1)
	buffer.Read(action)
	tmi.Action = string(action)
	var l2 int32
	binary.Read(buffer, binary.BigEndian, &l2)
	if l2 > 0 {
		extra := make([]byte, l2)
		buffer.Read(extra)
		tmi.Extra = string(extra)
	}
	return true
}

type ThroughMessageOut struct {
	Sender    int64
	Receiver  int64
	Timestamp int32
	Msgid     int64
	Action   string
	Extra     string
}

func (tmo *ThroughMessageOut) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, tmo.Sender)
	binary.Write(buffer, binary.BigEndian, tmo.Receiver)
	binary.Write(buffer, binary.BigEndian, tmo.Timestamp)
	binary.Write(buffer, binary.BigEndian, tmo.Msgid)
	l1 := int8(len(tmo.Action))
	binary.Write(buffer, binary.BigEndian, l1)
	buffer.Write([]byte(tmo.Action))
	l2 := int32(len(tmo.Extra))
	binary.Write(buffer, binary.BigEndian, l2)
	if l2 > 0 {
		buffer.Write([]byte(tmo.Extra))
	}
	buf := buffer.Bytes()
	return buf
}

func (tmo *ThroughMessageOut) FromData(buff []byte) bool {
	if len(buff) < 28 {
		return false
	}
	
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &tmo.Sender)
	binary.Read(buffer, binary.BigEndian, &tmo.Receiver)
	binary.Read(buffer, binary.BigEndian, &tmo.Timestamp)
	binary.Read(buffer, binary.BigEndian, &tmo.Msgid)
	var l1 int8
	binary.Read(buffer, binary.BigEndian, &l1)
	action := make([]byte, l1)
	buffer.Read(action)
	tmo.Action = string(action)
	var l2 int32
	binary.Read(buffer, binary.BigEndian, &l2)
	if l2 > 0 {
		extra := make([]byte, l2)
		buffer.Read(extra)
		tmo.Extra = string(extra)
	}
	return true
}

type Authentication struct {
	Uid         int64
}

func (auth *Authentication) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, auth.Uid)
	buf := buffer.Bytes()
	return buf
}

func (auth *Authentication) FromData(buff []byte) bool {
	if len(buff) < 8 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &auth.Uid)
	return true
}

type AuthenticationToken struct {
	Uid			int64
	Token       string
	Platform_id int8
	Device_id   string
}


func (auth *AuthenticationToken) ToData() []byte {
	var l int8

	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, auth.Platform_id)
	binary.Write(buffer, binary.BigEndian, auth.Uid)

	l = int8(len(auth.Token))
	binary.Write(buffer, binary.BigEndian, l)
	buffer.Write([]byte(auth.Token))

	l = int8(len(auth.Device_id))
	binary.Write(buffer, binary.BigEndian, l)
	buffer.Write([]byte(auth.Device_id))

	buf := buffer.Bytes()
	return buf
}

func (auth *AuthenticationToken) FromData(buff []byte) bool {
	var l int8
	if (len(buff) <= 11) {
		return false
	}
	auth.Platform_id = int8(buff[0])

	buffer := bytes.NewBuffer(buff[1:])

	binary.Read(buffer, binary.BigEndian, &auth.Uid)
	binary.Read(buffer, binary.BigEndian, &l)
	if int(l) > buffer.Len() {
		return false
	}
	token := make([]byte, l)
	buffer.Read(token)

	binary.Read(buffer, binary.BigEndian, &l)
	if int(l) > buffer.Len() {
		return false
	}
	device_id := make([]byte, l)
	buffer.Read(device_id)

	auth.Token = string(token)
	auth.Device_id = string(device_id)
	return true
}

type AuthenticationStatus struct {
	Status int32
}

func (auth *AuthenticationStatus) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, auth.Status)
	buf := buffer.Bytes()
	return buf
}

func (auth *AuthenticationStatus) FromData(buff []byte) bool {
	if len(buff) < 4 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &auth.Status)
	return true
}


type DeviceKick struct {
	Uid      		  int64
	Platform_id       int8
	Device_id         string
}

func (dk *DeviceKick) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, dk.Uid)
	binary.Write(buffer, binary.BigEndian, dk.Platform_id)
	buffer.Write([]byte(dk.Device_id))
	buf := buffer.Bytes()
	return buf
}

func (dk *DeviceKick) FromData(buff []byte) bool {
	if len(buff) <= 9 {
		return false
	}

	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &dk.Uid)
	binary.Read(buffer, binary.BigEndian, &dk.Platform_id)
	dk.Device_id = string(buff[9:])
	return true
}


type MessageACK struct {
	Seq int64
}

func (ack *MessageACK) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, ack.Seq)
	buf := buffer.Bytes()
	return buf
}

func (ack *MessageACK) FromData(buff []byte) bool {
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &ack.Seq)
	return true
}

type MessagePeerACK struct {
	Sender   int64
	Receiver int64
	Msgid    int64
}

func (ack *MessagePeerACK) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, ack.Sender)
	binary.Write(buffer, binary.BigEndian, ack.Receiver)
	binary.Write(buffer, binary.BigEndian, ack.Msgid)
	buf := buffer.Bytes()
	return buf
}

func (ack *MessagePeerACK) FromData(buff []byte) bool {
	if len(buff) < 20 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &ack.Sender)
	binary.Read(buffer, binary.BigEndian, &ack.Receiver)
	binary.Read(buffer, binary.BigEndian, &ack.Msgid)
	return true
}

type MessageInputing struct {
	Sender   int64
	Receiver int64
}

func (inputing *MessageInputing) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, inputing.Sender)
	binary.Write(buffer, binary.BigEndian, inputing.Receiver)
	buf := buffer.Bytes()
	return buf
}

func (inputing *MessageInputing) FromData(buff []byte) bool {
	if len(buff) < 16 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &inputing.Sender)
	binary.Read(buffer, binary.BigEndian, &inputing.Receiver)
	return true
}

type GroupNotification struct {
	Notification string
}

func (notification *GroupNotification) ToData() []byte {
	return []byte(notification.Notification)
}
 
func (notification *GroupNotification) FromData(buff []byte) bool {
	notification.Notification = string(buff)
	return true
}

type MessageOnlineState struct {
	Sender int64
	Online int32
}

func (state *MessageOnlineState) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, state.Sender)
	binary.Write(buffer, binary.BigEndian, state.Online)
	buf := buffer.Bytes()
	return buf
}

func (state *MessageOnlineState) FromData(buff []byte) bool {
	if len(buff) < 12 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &state.Sender)
	binary.Read(buffer, binary.BigEndian, &state.Online)
	return true
}

type AppUserID struct {
	Uid      int64
}

func (id *AppUserID) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, id.Uid)
	buf := buffer.Bytes()
	return buf
}

func (id *AppUserID) FromData(buff []byte) bool {
	if len(buff) < 8 {
		return false
	}

	buffer := bytes.NewBuffer(buff)	
	binary.Read(buffer, binary.BigEndian, &id.Uid)

	return true
}

type FriendOp struct {
	Sender int64
	Receiver int64
}

func (fo *FriendOp) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, fo.Sender)
	binary.Write(buffer, binary.BigEndian, fo.Receiver)
	buf := buffer.Bytes()
	return buf
}

func (fo *FriendOp) FromData(buff []byte) bool {
	if len(buff) < 16 {
		return false
	}
	
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &fo.Sender)
	binary.Read(buffer, binary.BigEndian, &fo.Receiver)
	
	return true
} 

type FriendOpStatus struct {
	Status int32
	Sender int64
	Receiver int64
	Msgid int64
}

func (fos *FriendOpStatus) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, fos.Status)
	binary.Write(buffer, binary.BigEndian, fos.Sender)
	binary.Write(buffer, binary.BigEndian, fos.Receiver)
	binary.Write(buffer, binary.BigEndian, fos.Msgid)
	buf := buffer.Bytes()
	return buf
}

func (fos *FriendOpStatus) FromData(buff []byte) bool {
	if len(buff) < 28 {
		return false
	}
	
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &fos.Status)
	binary.Read(buffer, binary.BigEndian, &fos.Sender)
	binary.Read(buffer, binary.BigEndian, &fos.Receiver)
	binary.Read(buffer, binary.BigEndian, &fos.Msgid)
	
	return true
}

type UserBaseInfo struct {
	Uid    int64
	Username  string
	Avatar	  string
}

func (ubi *UserBaseInfo) ToData() []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, ubi.Uid)
	l1 := int32(len(ubi.Username))
	binary.Write(buffer, binary.BigEndian, l1)
	l2 := int32(len(ubi.Avatar))
	binary.Write(buffer, binary.BigEndian, l2)
	if l1 > 0 {
		buffer.Write([]byte(ubi.Username))
	}
	
	if l2 > 0 {
		buffer.Write([]byte(ubi.Avatar))
	}
	
	buf := buffer.Bytes()
	return buf
}

func (ubi *UserBaseInfo) FromData(buff []byte) bool {
	if len(buff) < 8 {
		return false
	}
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &ubi.Uid)
	var l1, l2 int32
	binary.Read(buffer, binary.BigEndian, &l1)
	binary.Read(buffer, binary.BigEndian, &l2)
	if l1 > 0 {
		nameBytes := make([]byte, l1)
		buffer.Read(nameBytes)
		ubi.Username = string(nameBytes)
	}
	
	if l2 > 0 {
		avatarBytes := make([]byte, l2)
		buffer.Read(avatarBytes)
		ubi.Avatar = string(avatarBytes)
	}

	return true
}

type FriendList struct {
	Number int32
	Users []*UserBaseInfo
}

func (fl *FriendList) ToData() []byte {
	buffer := new (bytes.Buffer)
	binary.Write(buffer, binary.BigEndian, fl.Number)
	for i:=0; i<int(fl.Number); i++ {
		userBytes := fl.Users[i].ToData()
		l := int32(len(userBytes))
		binary.Write(buffer, binary.BigEndian, l)
		buffer.Write(userBytes)
	}
	
	return buffer.Bytes()
}

func (fl *FriendList) FromData(buff []byte) bool {
	if len(buff) < 4 {
		return false
	}
	
	buffer := bytes.NewBuffer(buff)
	binary.Read(buffer, binary.BigEndian, &fl.Number)
	
	if fl.Number > 0 {
		fl.Users = make([]*UserBaseInfo, fl.Number)
	
		var l int32
		for i:=0; i<int(fl.Number); i++ {
			binary.Read(buffer, binary.BigEndian, &l)
			userBytes := make([]byte, l)
			buffer.Read(userBytes)
			
			ubi := new(UserBaseInfo)
			if ubi.FromData(userBytes) {
				fl.Users = append(fl.Users, ubi)
			}
		}
	}
	
	return true
}

func SendMessage(conn io.Writer, msg *Message) error {
	body := msg.ToData()
	buffer := new(bytes.Buffer)
	WriteHeader(int32(len(body)), int64(msg.Seq), byte(msg.Cmd), byte(msg.Version), buffer)
	buffer.Write(body)
	buf := buffer.Bytes()
	n, err := conn.Write(buf)
	if err != nil {
		log.Info("sock write error:", err)
		return err
	}
	if n != len(buf) {
		log.Infof("write less:%d %d", n, len(buf))
		return errors.New("write less")
	}
	return nil
}

func ReceiveMessage(conn io.Reader) *Message {
	buff := make([]byte, 16)
	_, err := io.ReadFull(conn, buff)
	if err != nil {
		log.Info("sock read error:", err)
		return nil
	}

	length, seq, cmd, version := ReadHeader(buff)
	if length < 0 || length > 64*1024 {
		log.Info("invalid len:", length)
		return nil
	}
	buff = make([]byte, length)
	_, err = io.ReadFull(conn, buff)
	if err != nil {
		log.Info("sock read error:", err)
		return nil
	}
	log.Infof("length:%d,seq:%d,cmd:%d,version:%d", length, seq, cmd, version)
	message := new(Message)
	message.Cmd = cmd
	message.Seq = seq
	message.Version = version
	if !message.FromData(buff) {
		log.Warning("parse error")
		return nil
	}
	return message
}

func NewRedisPool(server, password string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     100,
		MaxActive:   500,
		IdleTimeout: 480 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			if len(password) > 0 {
				if _, err := c.Do("AUTH", password); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
	}
}